package cn.yzgy.yzgyoa.common.crsp;

import com.alibaba.fastjson.JSONObject;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * Unified response message format
 * return a JSON Object for client with unified message format
 *
 * @author Yqsen
 * @version 0.1.1
 * @since 2019-6-3 8:47:32
 * @see cn.yzgy.yzgyoa.common.crsp.StatusCode
 */

@SuppressWarnings(value = {"unused", "WeakerAccess"})
public class Urmf extends JSONObject {

    /**
     * JSON键名定义
     * operation: 当前请求API的名字或API路径
     * success: 成功标志位
     * code: 状态码
     * msg: 状态码对应的消息
     * time: 返回时间，通常格式为：yyyy-MM-dd HH:mm:ss
     * data: 返回数据
     */
    private static final String KEY_NAME_OPERATION = "operation";
    private static final String KEY_NAME_SUCCESS_FLAG = "success";
    private static final String KEY_NAME_STATUS_CODE = "code";
    private static final String KEY_NAME_STATUS_MSG = "msg";
    private static final String KEY_NAME_TIME = "time";
    private static final String KEY_NAME_DATA = "data";

    /**
     * 键默认值定义
     */
    private static final String KEY_VAL_OPERATION = "default";
    private static final Boolean KEY_VAL_SUCCESS_FLAG = true;
    private static final StatusCode KEY_VAL_STATUS_CODE_FAILED = StatusCode.FAILED;
    private static final StatusCode KEY_VAL_STATUS_CODE_SUCCESS = StatusCode.SUCCESS;

    /**
     * 全参数构造函数
     *
     * @param operation  操作名
     * @param success    成功标志
     * @param statusCode 状态
     * @param data       数据
     */
    private Urmf(String operation, boolean success, StatusCode statusCode, Object data) {
        this.put(KEY_NAME_OPERATION, operation);
        this.put(KEY_NAME_SUCCESS_FLAG, success);
        this.put(KEY_NAME_STATUS_CODE, statusCode.getCode());
        this.put(KEY_NAME_STATUS_MSG, statusCode.getMsg());
        this.put(KEY_NAME_TIME, LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        if (data != null) {
            this.put(KEY_NAME_DATA, data);
        }
    }

    /**
     * 不带数据的构造函数
     *
     * @param operation  操作名
     * @param success    成功标志
     * @param statusCode 状态
     */
    private Urmf(String operation, boolean success, StatusCode statusCode) {
        this(operation, success, statusCode, null);
    }

    /**
     * 只戴方法名的构造函数，默认返回成功
     *
     * @param operation 操作名
     */
    private Urmf(String operation) {
        this(operation, KEY_VAL_SUCCESS_FLAG, KEY_VAL_STATUS_CODE_SUCCESS);
    }

    /**
     * 无参构造函数，默认成功
     */
    private Urmf() {
        this(KEY_VAL_OPERATION);
    }

    /*
     * 静态工厂方法，快速获取返回数据 ↓
     */

    /**
     * 无参方法，返回默认（成功）消息
     *
     * @return 默认成功消息
     */
    public static Urmf success() {
        return new Urmf();
    }

    /**
     * 带操作方法名称参数的成功消息
     *
     * @param operation 操作名
     * @return 带操作方法名称参数的成功消息
     */
    public static Urmf success(String operation) {
        return new Urmf(operation);
    }

    /**
     * 带操作方法名和自定义状态码的成功返回消息
     *
     * @param operation  操作名
     * @param statusCode 状态码
     * @return 带操作方法名和自定义状态码的成功返回消息
     */
    public static Urmf success(String operation, StatusCode statusCode) {
        return new Urmf(operation, true, statusCode);
    }

    /**
     * 带操作方法名和返回数据的成功返回消息
     *
     * @param operation 操作名
     * @param data      数据
     * @return 带操作方法名和返回数据的成功返回消息
     */
    public static Urmf success(String operation, Object data) {
        return new Urmf(operation).data(data);
    }

    /**
     * 带操作方法名、状态码、返回数据的成功返回消息
     *
     * @param operation  操作名
     * @param statusCode 状态码
     * @param data       数据
     * @return 带操作方法名、状态码、返回数据的成功返回消息
     */
    public static Urmf success(String operation, StatusCode statusCode, Object data) {
        return new Urmf(operation, true, statusCode, data);
    }

    /*
     * 失败的情况
     */

    /**
     * 无参方法，返回默认失败消息
     *
     * @return 默认失败消息
     */
    public static Urmf fail() {
        return new Urmf(KEY_VAL_OPERATION, false, KEY_VAL_STATUS_CODE_FAILED);
    }

    /**
     * 带操作方法名称参数的失败消息
     *
     * @param operation 操作名
     * @return 带操作方法名称参数的失败消息
     */
    public static Urmf fail(String operation) {
        return new Urmf(operation, false, KEY_VAL_STATUS_CODE_FAILED);
    }

    /**
     * 带操作方法名和自定义状态码的失败返回消息
     *
     * @param operation  操作名
     * @param statusCode 状态码
     * @return 带操作方法名和自定义状态码的失败返回消息
     */
    public static Urmf fail(String operation, StatusCode statusCode) {
        return new Urmf(operation, false, statusCode);
    }

    /**
     * 带操作方法名、返回数据的失败返回消息
     *
     * @param operation 操作名
     * @param data      数据
     * @return 带操作方法名、返回数据的失败返回消息
     */
    public static Urmf fail(String operation, Object data) {
        return new Urmf(operation, false, KEY_VAL_STATUS_CODE_FAILED, data);
    }

    /**
     * 带操作方法名、状态码、返回数据的失败返回消息
     *
     * @param operation  操作名
     * @param statusCode 状态码
     * @param data       数据
     * @return 带操作方法名、状态码、返回数据的失败返回消息
     */
    public static Urmf fail(String operation, StatusCode statusCode, Object data) {
        return new Urmf(operation, false, statusCode, data);
    }

    /**
     * 设置操作名
     *
     * @param operation 操作名
     * @return this
     */
    public Urmf operation(String operation) {
        this.put(KEY_NAME_OPERATION, operation);
        return this;
    }

    /**
     * 设置成功标志位
     *
     * @param success 成功标志位
     * @return this
     */
    public Urmf successFlag(Boolean success) {
        this.put(KEY_NAME_SUCCESS_FLAG, success);
        return this;
    }

    /**
     * 设置状态码
     *
     * @param statusCode 状态码
     * @return this
     */
    public Urmf statusCode(StatusCode statusCode) {
        this.put(KEY_NAME_STATUS_MSG, statusCode.getMsg());
        this.put(KEY_NAME_STATUS_CODE, statusCode.getCode());
        return this;
    }

    /**
     * 设置数据域
     *
     * @param data 数据
     * @return this
     */
    public Urmf data(Object data) {
        this.put(KEY_NAME_DATA, data);
        return this;
    }

    /**
     * 添加新的节与值
     *
     * @param key 键名
     * @param val 键值
     * @return this
     */
    public Urmf newData(String key, Object val) {
        this.put(key, val);
        return this;
    }
}
